package org.xteam.sled.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.xteam.sled.semantic.Field;
import org.xteam.sled.semantic.exp.Exp;
import org.xteam.sled.semantic.exp.VariableSubstitute;

public class LabelledSequence extends BaseSequence<ILabelledSequent> {
	
	public LabelledSequence(boolean hasPrefix, boolean hasSuffix, List<ILabelledSequent> elements) {
		super(hasPrefix, elements, hasSuffix);
	}

	public LabelledSequence(List<ILabelledSequent> elements) {
		super(false, elements, false);
	}
	
	public LabelledSequence(ILabelledSequent element) {
		super(element);
	}
	
	public LabelledSequence() {
		this(new ArrayList<ILabelledSequent>());
	}
	
	@Override
	protected BaseSequence<ILabelledSequent> createSequence() {
		return (BaseSequence<ILabelledSequent>) new LabelledSequence();
	}

	public AbsoluteSequence anf(Map<AbsoluteField, Exp> bindings) {
		int n = 0;
		List<RangeConstraint<AbsoluteField>> constraints = new ArrayList<RangeConstraint<AbsoluteField>>();
		Map<String, Integer> labels = new HashMap<String, Integer>();
		for (ILabelledSequent r : elements) {
			if (r.isLabel()) {
				Label xl = (Label) r;
				if (xl.isActual()) {
					String name = ((ActualLabel) xl).name();
					if (labels.containsKey(name)) {
						throw new RuntimeException("pattern label " + name
								+ " appears twice in disjunct");
					}
					labels.put(name, n);
				} else {
					throw new RuntimeException(
							"impossible: latent label in anf conversion");
				}
			} else {
				Constraints xc = (Constraints) r;
				for (Field f : xc.fieldBindings.keySet()) {
					bindings.put(new AbsoluteField(f, n), xc.fieldBindings.get(f));
				}
				for (RangeConstraint<Field> b : xc.rangeConstraints) {
					constraints.add(new RangeConstraint<AbsoluteField>(
							new AbsoluteField(b.field, n), b.range));
				}
				n += xc.tokenClass.size();
			}
		}
		return new AbsoluteSequence(constraints, labels, n);
	}

	@Override
	public ISequence createSequence(boolean hasPrefix, boolean hasSuffix,
			List<ISequent> elements) {
		List<ILabelledSequent> n = new ArrayList<ILabelledSequent>();
		for (ISequent e : elements) {
			n.add((ILabelledSequent) e);
		}
		return new LabelledSequence(hasPrefix, hasSuffix, n);
	}

	public Map<String, Exp> labelOffsets() {
		Map<String, Exp> bindings = new HashMap<String, Exp>();
		int n = 0;
		for (ILabelledSequent element : elements) {
			if (element.isLabel()) {
				((Label) element).addBinding(bindings, n);
			} else {
				n += element.width();
			}
		}
		return bindings;
	}
	
	public LatentSequence makePCRelative(VariableSubstitute subs) {
		List<ILatentSequent> newElements = new ArrayList<ILatentSequent>();
		for (ILabelledSequent element : elements) {
			if (! element.isLabel()) {
				newElements.add((ILatentSequent)element.subs(subs));
			} else if (! ((Label)element).isActual()) {
				newElements.add((ILatentSequent)element.subs(subs));
			}
		}
		return new LatentSequence(hasPrefix, newElements, hasSuffix);
	}

	public LabelledSequence actualize(boolean actualsOk) {
		List<ILabelledSequent> newElements = new ArrayList<ILabelledSequent>();
		for (ILabelledSequent element : elements) {
			ILabelledSequent ls = element.actualize(actualsOk);
			if (ls != null)
				newElements.add(ls);
		}
		return new LabelledSequence(hasPrefix, hasSuffix, newElements);
	}

}
